package com.pig4cloud.pig.djjk.service.wd;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.pig4cloud.pig.djjk.constant.MoveDirection;
import com.pig4cloud.pig.djjk.domain.entity.XCCJZP;
import com.pig4cloud.pig.djjk.domain.po.Alert;
import com.pig4cloud.pig.djjk.domain.po.FaceLibSearchResp;
import com.pig4cloud.pig.djjk.entity.TrackRecordEntity;
import com.pig4cloud.pig.djjk.mapper.TrackRecordMapper;
import com.pig4cloud.pig.djjk.mapper.XCCJZPMapper;
import com.pig4cloud.pig.djjk.service.immi.CutService;
import com.pig4cloud.pig.djjk.service.immi.FaceApiService;
import com.pig4cloud.pig.djjk.service.immi.FaceDetectService;
import com.pig4cloud.pig.djjk.service.immi.TraceService;
import com.wd.ais.domain.dto.DetectFaceQualityRequest;
import com.wd.ais.domain.dto.DetectFaceQualityResponse;
import com.wd.ais.service.AisToolsService;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.File;
import java.util.*;
import java.util.logging.Logger;

/**
 * @className: WdTrackService
 * @description: TODO 类描述
 * @author: liumingkun
 * @date: 2025/6/25
 **/
@Service
public class WdTrackService {

	Logger log = Logger.getLogger(WdTrackService.class.getName());

	@Resource
	FaceDetectService faceDetectService;

	@Resource
	TraceService traceService;

	@Resource
	CutService cutService;

	@Resource
	AisToolsService aisToolsService;

	@Resource
	DirJudgeService dirJudgeService;

	@Resource
	private FaceApiService faceApiService;

	@Resource
	XCCJZPMapper xccjzpMapper;

	@Resource
	TrackRecordMapper trackRecordMapper;


	//是否通行数据写入数据库
	public static boolean WRITE_STATE = true;

	//人脸质量超过这个分数才会存储
	public static double FACE_QUALITY_THRESH_HOLD = 0.9;

	//徘徊时间，人员徘徊超过这个时间，即被统计，另一个功能是超过这个时间没有被抓拍到，则绘制轨迹并入库
	private final static Long WANDER_TIME = 1000 * 30l;

	//舷梯刷新时间，超出这个时间没有发现舷梯，刚清理舷梯信息
	private final static Long STAIR_REFRESH_DURATION = 1000 * 60 * 3l;

	//private List<Integer> stairLocation = Arrays.asList(201, 11, 760, 711);
	//初始舷梯位置，区域很小，保证人员不会出现在舷梯区域内
	// private List<Integer> stairLocation = Arrays.asList(0, 0, 2, 2);
	public Map<String, List<Integer>> stairLocation = new HashMap<>();

	//记录舷梯抓拍时候，如果3分钟内没有抓拍，则把舷梯信息清除
	public Map<String, Long> lastStairTime = new HashMap<>();

	//记录人员最后一次抓拍时间，1分钟（wanderTime）没有抓拍，开始绘制轨迹
	public Map<String, Long> lastCaptureTimeMap = new HashMap<>();

	//抓拍的人脸照片
	public Map<String, String> faceMap = new HashMap<>();

	//场景照片
	public Map<String, String> sceneMap = new HashMap<>();

	//人员轨迹点位
	public Map<String, List<List<Integer>>> tracePointMap = new HashMap<>();

	//判断人员是否出现在舷梯，如果出现在舷梯，则存储人员的位置图片
	public Map<String, Boolean> inLadderMap = new HashMap<>();

	//存储舷梯照片人员的位置信息
	public Map<String, List<Integer>> frameMap = new HashMap<>();


	/**
	 * 存储舷梯位置
	 *
	 * @param eventObj
	 */
	public void ladderPositionHandler(JSONObject eventObj) {
		Alert alert = JSONUtil.toBean(eventObj, Alert.class);
		lastStairTime.put(alert.getSource().getId(), System.currentTimeMillis());
		stairLocation.put(alert.getSource().getId(), alert.getReserved_data().getBbox().getRectangles().get(0).getXyxy());
	}

	/**
	 * 开始清理舷梯信息
	 */
	public void clearLadderPosition() {
		Map<String, Long> tempMap = new HashMap<>(lastStairTime);
		List<String> toRemoveKeys = new ArrayList<>();

		tempMap.entrySet().stream()
				.filter(entry -> entry.getValue() != null)
				.forEach(entry -> processStairState(entry.getKey(), entry.getValue(), toRemoveKeys));

		cleanStair(toRemoveKeys);
	}

	/**
	 * 计算需要清理的舷梯
	 *
	 * @param key
	 * @param lastCaptureTime
	 * @param toRemoveKeys
	 */
	public void processStairState(String key, long lastCaptureTime, List<String> toRemoveKeys) {
		long duration = System.currentTimeMillis() - lastCaptureTime;
		if (duration <= STAIR_REFRESH_DURATION) {
			log.info(String.format("%s : %s ms,不需要清理舷梯位置信息!", key, duration));
			return;
		} else {
			toRemoveKeys.add(key);
		}

	}

	/**
	 * 清理舷梯信息
	 *
	 * @param toRemoveKeys
	 */
	private void cleanStair(List<String> toRemoveKeys) {
		if (CollectionUtil.isEmpty(toRemoveKeys)) {
			return;
		}
		stairLocation.keySet().removeAll(toRemoveKeys);
	}


	/**
	 * 处理人员徘徊预警
	 *
	 * @param eventObj
	 */
	public void traceHandler(JSONObject eventObj) {
		log.info("开始处理徘徊事件");
		Alert alert = JSONUtil.toBean(eventObj, Alert.class);
		Map<String, Alert.CustomItem> cusMap = alert.getReserved_data().getCustom();

		for (Map.Entry<String, Alert.CustomItem> entry : cusMap.entrySet()) {
			String key = entry.getKey();
			Alert.CustomItem customPosition = entry.getValue();
			String deviceKey = alert.getSource().getId() + "_" + key;

			log.info(String.format("自定义位置数据: %s", JSONUtil.toJsonStr(customPosition)));

			processFaceDetection(alert, deviceKey);
			updateLastCaptureTime(deviceKey);
			updateSceneImage(alert, deviceKey);
			updateTracePoints(deviceKey, customPosition);
			checkLadderArea(alert, deviceKey, customPosition);
		}
	}

	/**
	 * 处理人脸信息，图片质量超出FACE_QUALITY_THRESH_HOLD才会被记录
	 *
	 * @param alert
	 * @param deviceKey
	 */
	private void processFaceDetection(Alert alert, String deviceKey) {
		String facePic = faceDetectService.getFacePic(alert.getImage());
		if (StringUtils.isNotEmpty(facePic)) {
			try {
				DetectFaceQualityRequest request = new DetectFaceQualityRequest();
				request.setImageBase64("data:image/png;base64," + facePic);
				DetectFaceQualityResponse response = aisToolsService.detectFaceQuality(request);
				Float quality = response.getQuality();
				log.info(String.format("人脸图片质量: %s", quality));
				if (quality > FACE_QUALITY_THRESH_HOLD) {
					faceMap.put(deviceKey, facePic);
				}
			} catch (Exception e) {
				log.info(String.format("图片质量识别失败: %s", e.getMessage()));
			}
		}
	}

	/**
	 * 更新人员被抓拍时间
	 *
	 * @param deviceKey
	 */
	private void updateLastCaptureTime(String deviceKey) {
		lastCaptureTimeMap.put(deviceKey, System.currentTimeMillis());
	}

	/**
	 * 更新场景图片
	 *
	 * @param alert
	 * @param deviceKey
	 */
	private void updateSceneImage(Alert alert, String deviceKey) {
		if (!sceneMap.containsKey(deviceKey)) {
			sceneMap.put(deviceKey, alert.getImage());
		}
	}

	/**
	 * 更新人员坐标
	 *
	 * @param deviceKey
	 * @param customPosition
	 */
	private void updateTracePoints(String deviceKey, Alert.CustomItem customPosition) {
		List<List<Integer>> tracePoints = tracePointMap.computeIfAbsent(deviceKey, k -> new ArrayList<>());
		List<Integer> lastPosition = customPosition.getCurrent_position();
		int px = (lastPosition.get(0) + lastPosition.get(2)) / 2;
		int py = (lastPosition.get(1) + lastPosition.get(3)) / 2;
		tracePoints.add(Arrays.asList(px, py));
	}

	/**
	 * 检测人员是否出现在舷梯，如果出现在舷梯，就更新场景图
	 *
	 * @param alert
	 * @param deviceKey
	 * @param customPosition
	 */
	private void checkLadderArea(Alert alert, String deviceKey, Alert.CustomItem customPosition) {
		boolean isInLadder = inLadderMap.getOrDefault(deviceKey, false);
		List<Integer> regions = stairLocation.get(alert.getSource().getId());

		if (CollectionUtil.isNotEmpty(regions) && (!isInLadder || dirJudgeService.isInsideRegion(regions, customPosition.getCurrent_position()))) {
			boolean insideRegion = dirJudgeService.isInsideRegion(regions, customPosition.getCurrent_position());
			inLadderMap.put(deviceKey, insideRegion);

			if (insideRegion) {
				int py = (customPosition.getCurrent_position().get(1) + customPosition.getCurrent_position().get(3)) / 2;
				log.info(String.format("%s出现在舷梯区域，纵向坐标: %s", deviceKey, py));

				if (sceneMap.get(deviceKey) == null || (py > 150 && py < 280)) {
					if (sceneMap.get(deviceKey) != null) {
						log.info("覆盖原场景图");
					}
					sceneMap.put(deviceKey, alert.getImage());
					frameMap.put(deviceKey, customPosition.getCurrent_position());
				}
			}
		}
	}


	public void addTrackRecord() {
		if (CollectionUtil.isEmpty(lastCaptureTimeMap)) {
			log.info("没有需要写入的数据!");
			return;
		}

		Map<String, Long> tempMap = new HashMap<>(lastCaptureTimeMap);
		List<String> toRemoveKeys = new ArrayList<>();

		tempMap.entrySet().stream()
				.filter(entry -> entry.getValue() != null)
				.forEach(entry -> processRecord(entry.getKey(), entry.getValue(), toRemoveKeys));

		cleanupData(toRemoveKeys);
	}

	private void processRecord(String key, Long lastCaptureTime, List<String> toRemoveKeys) {
		long duration = System.currentTimeMillis() - lastCaptureTime;
		if (duration <= WANDER_TIME) {
			log.info(String.format("%s : %s ms,不需要存储!", key, duration));
			return;
		}

		String scenePic = sceneMap.get(key);
		if (StringUtils.isEmpty(scenePic)) {
			log.info(String.format("%s : 没有场景图片!", key));
			toRemoveKeys.add(key);
			return;
		}

		XCCJZP xccjzp = createXCCJZP(key);
		List<List<Integer>> points = tracePointMap.get(key);

		if (points == null) {
			log.info(String.format("Key %s has null trace points", key));
			toRemoveKeys.add(key);
			return;
		}

		processTraceData(key, scenePic, points, xccjzp);

		if (!xccjzp.getCRLX().equals("3")) {
			processFaceRecognition(key, xccjzp);
		}

		toRemoveKeys.add(key);
		logRemovalInfo(key, xccjzp);
	}

	private XCCJZP createXCCJZP(String key) {
		XCCJZP xccjzp = new XCCJZP();
		xccjzp.setCJSJ(DateUtil.format(new Date(), "yyyyMMddHHmmss"));
		xccjzp.setSBBH(key.substring(0, key.indexOf("_")));
		return xccjzp;
	}

	private void processTraceData(String key, String scenePic, List<List<Integer>> points, XCCJZP xccjzp) {
		xccjzp.setCRLX(checkMoveDirection(points, key));
		log.info(String.format("%s : %s", key, JSONUtil.toJsonStr(points)));

		String tracePic = traceService.getTracePic(scenePic, points);
		byte[] traceBytes = Base64.getDecoder().decode(tracePic);
		//FileUtil.writeBytes(traceBytes, new File("/data/djjk/images/" + key + "output.png"));
		FileUtil.writeBytes(traceBytes, new File("D:\\trace-test\\" + key + "output.png"));
		xccjzp.setBJZP(traceBytes);
	}

	private void processFaceData(String key, XCCJZP xccjzp) {

		byte[] faceBytes = xccjzp.getZPZP();
		//FileUtil.writeBytes(faceBytes, new File("/data/djjk/images/" + key + "face.png"));
		FileUtil.writeBytes(faceBytes, new File("D:\\trace-test\\" + key + "face.png"));

	}

	private void processFaceRecognition(String key, XCCJZP xccjzp) {
		byte[] zpzp = xccjzp.getZPZP();
		if (ArrayUtil.isNotEmpty(zpzp)) {
			log.info("写入步骤: 1");
			FaceLibSearchResp faceLibSearchResp = faceApiService.faceLibSearch(
					Base64.getEncoder().encodeToString(zpzp));
			List<Integer> framePoints = frameMap.get(key);
			if (faceLibSearchResp == null) {
				log.info("未识别到人脸信息，开始处理人体信息！");
				handleFaceRecognitionFailure(xccjzp, framePoints);
			}
			String rybh = faceLibSearchResp.getRybh();
			if (StrUtil.isEmpty(rybh)) {
				handleEmptyRybh(key, xccjzp, framePoints);
			}
			xccjzp.setRYBH(rybh);
			xccjzp.setXSD(Convert.toStr(faceLibSearchResp.getScore()));
			log.info("写入步骤: 2");
		} else {
			List<Integer> framePoints = frameMap.get(key);
			handleFaceRecognitionFailure(xccjzp, framePoints);
		}


		if (WRITE_STATE) {
			if (ArrayUtils.isEmpty(xccjzp.getZPZP())) {
				log.info("抓拍照片为空，不需要写入数据库！");
			}
			{
				processFaceData(key, xccjzp);
				log.info("写入步骤: 3");
				//writeToDataBase(xccjzp);
			}
		} else {
			log.info("写入数据库开关关闭！");
		}


	}

	public void writeToDataBase(XCCJZP xccjzp) {

		log.info("写入步骤: 4");
		xccjzpMapper.insert(xccjzp);
		xccjzpMapper.callDJJK();

		log.info("写入步骤: 5");
		TrackRecordEntity trackRecordEntity = new TrackRecordEntity();
		trackRecordEntity.setDeviceCode(xccjzp.getSBBH());
		trackRecordEntity.setDataSource("wdbox");
		trackRecordEntity.setDataType(xccjzp.getCRLX());
		trackRecordEntity.setPersonCode(xccjzp.getRYBH());
		trackRecordEntity.setIsMatch(StrUtil.isNotBlank(xccjzp.getRYBH()) ? "1" : "0");
		trackRecordEntity.setZpid(xccjzp.getZPID());
		trackRecordMapper.insert(trackRecordEntity);
		log.info("写入步骤: 5");
	}

	private void handleFaceRecognitionFailure(XCCJZP xccjzp, List<Integer> framePoints) {
		if (CollectionUtil.isEmpty(framePoints)) {
			log.info("人员位置信息为空!");
			return;
		}

		String tracePic = Base64.getEncoder().encodeToString(xccjzp.getBJZP());
		String cutPic = cutService.getCutPic(tracePic, framePoints);
		byte[] faceBytes = Base64.getDecoder().decode(cutPic);
		xccjzp.setZPZP(faceBytes);
		log.info("存储人体图片");
	}

	private void handleEmptyRybh(String key, XCCJZP xccjzp, List<Integer> framePoints) {
		log.info("人脸库搜索失败，人脸识别失败2");
		if (CollectionUtil.isEmpty(framePoints)) {
			log.info("没有人员信息，不写入数据库(2)");
			return;
		}

		String tracePic = Base64.getEncoder().encodeToString(xccjzp.getBJZP());
		String cutPic = cutService.getCutPic(tracePic, framePoints);
		if (StringUtils.isEmpty(cutPic)) {
			log.info("人体图片为空，不写入数据库(3)");
			return;
		}

		byte[] faceBytes = Base64.getDecoder().decode(cutPic);
		xccjzp.setZPZP(faceBytes);
		xccjzp.setXSD("0");
		log.info("写入人体图片(3)");
	}

	private void logRemovalInfo(String key, XCCJZP xccjzp) {
		log.info(String.format("准备清理数据：%s", key));
		log.info(String.format("人员运动方向(%s): %s", key, xccjzp.getCRLX()));
		log.info("写入步骤: 0");
	}

	private void cleanupData(List<String> toRemoveKeys) {
		if (CollectionUtil.isEmpty(toRemoveKeys)) {
			return;
		}

		lastCaptureTimeMap.keySet().removeAll(toRemoveKeys);
		sceneMap.keySet().removeAll(toRemoveKeys);
		tracePointMap.keySet().removeAll(toRemoveKeys);
		frameMap.keySet().removeAll(toRemoveKeys);
		faceMap.keySet().removeAll(toRemoveKeys);
	}


	public String checkMoveDirection(List<List<Integer>> points, String deviceKey) {
		String deviceId = deviceKey.substring(0, deviceKey.indexOf("_"));
		List<Integer> stairs = stairLocation.get(deviceId);
		log.info(com.alibaba.fastjson2.JSONObject.toJSONString(stairs));

		String result = "3";
		if (CollectionUtil.isNotEmpty(points) && CollUtil.isNotEmpty(stairs)) {
			List<Integer> firstPosition = points.get(0);
			List<Integer> lastPosition = points.get(points.size() - 1);
			MoveDirection moveDirection = dirJudgeService.analyzeMovementByCenter(stairs, lastPosition, firstPosition);
			if (moveDirection.equals(MoveDirection.ENTER)) {
				//登轮
				result = "1";

			}
			if (moveDirection.equals(MoveDirection.EXIT)) {
				//离轮
				result = "2";
			}
			if (null != frameMap.get(deviceKey)) {
				if (firstPosition.get(1) - lastPosition.get(1) > 100) {
					log.info("根据起始位置和是否出现在舷梯判定为离轮");
					log.info(com.alibaba.fastjson2.JSONObject.toJSONString(points));
					result = "1";
				}
				if (firstPosition.get(1) - lastPosition.get(1) < -100) {
					log.info("根据起始位置和是否出现在舷梯判定为登轮");
					log.info(com.alibaba.fastjson2.JSONObject.toJSONString(points));
					result = "2";
				}
			}
		}
		//默认返回3,"徘徊"
		return result;
	}


}
